﻿using FlyingEye.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Volo.Abp.DependencyInjection;

namespace FlyingEye.Common.Utils
{
    public class ApplicationConfiguration : ISingletonDependency
    {
        private static Lazy<string>? RsaPrivateKeyLazy { get; set; }

        private static Lazy<string>? RsaPublicKeyLazy { get; set; }

        private string? RsaPrivateKey => RsaPrivateKeyLazy?.Value;

        private string? RsaPublicKey => RsaPublicKeyLazy?.Value;

        private readonly IConfiguration _configuration;

        private readonly ConcurrentTextReader _reader;

        /// <summary>
        /// 每次用户登录时，都要获取 Token，所以每次都要获取 Secret 加密返回 Token，所以在这里做缓存
        /// </summary>
        private static Lazy<string>? JwtSecretLazy { get; set; }

        public string? JwtSecret => JwtSecretLazy?.Value;

        private readonly ApplicationStartInOptions _startInfo;

        public ApplicationConfiguration(IConfiguration configuration, ConcurrentTextReader reader, IOptions<ApplicationStartInOptions> options)
        {
            _startInfo = options.Value;
            _reader = reader;
            _configuration = configuration;
            JwtSecretLazy = new Lazy<string>(GetJwtSecret);
            RsaPrivateKeyLazy = new Lazy<string>(GetRsaPrivateKey);
            RsaPublicKeyLazy = new Lazy<string>(GetRsaPublicKey);
        }

        /// <summary>
        /// 将明文进行加密，返回 Base64String 格式的加密字符串
        /// </summary>
        /// <param name="plainData">需要加密的明文</param>
        public string ConvertToEncryptedBase64String(string plainData)
        {
            if (string.IsNullOrWhiteSpace(RsaPublicKey))
            {
                throw new InvalidOperationException("无效的 RSA 公钥，请检查系统公钥文件。");
            }

            // 创建 RSA 实例，并加载公钥
            using (RSA rsa = RSA.Create())
            {
                rsa.ImportFromPem(RsaPublicKey);

                // 将明文数据转换为字节数组
                byte[] plainBytes = Encoding.UTF8.GetBytes(plainData);

                // 使用公钥进行加密
                byte[] encryptedBytes = rsa.Encrypt(plainBytes, RSAEncryptionPadding.Pkcs1);

                // 转换为可读的 base64 字符串。
                var encryptedDataBase64 = Convert.ToBase64String(encryptedBytes);

                return encryptedDataBase64;
            }
        }

        /// <summary>
        /// 将 base64 加密字符串转换为明文字符串
        /// </summary>
        /// <param name="base64String"> base64 字符串</param>
        public string ConvertFromEncryptedBase64String(string base64String)
        {
            if (string.IsNullOrWhiteSpace(RsaPrivateKey))
            {
                throw new InvalidOperationException("无效的 RSA 私钥，请检查系统公钥文件。");
            }

            // 创建 RSA 实例，并加载私钥
            using (RSA rsa = RSA.Create())
            {
                rsa.ImportFromPem(RsaPrivateKey);

                // 读取密文数据
                byte[] encryptedBytes = Convert.FromBase64String(base64String);

                // 使用私钥进行解密
                byte[] decryptedBytes = rsa.Decrypt(encryptedBytes, RSAEncryptionPadding.Pkcs1);

                // 将解密后的字节数组转换为字符串
                string decryptedData = Encoding.UTF8.GetString(decryptedBytes);

                return decryptedData;
            }
        }

        private string GetRsaPublicKey()
        {
            var section = _configuration.GetSection("RsaKey");
            var publicKeyFile = Path.Combine(_startInfo.StartPath, section["PublicKey"]);

            return _reader.ReadAllText(publicKeyFile);
        }

        private string GetRsaPrivateKey()
        {
            var section = _configuration.GetSection("RsaKey");
            var privateKeyFile = Path.Combine(_startInfo.StartPath, section["PrivateKey"]);

            return _reader.ReadAllText(privateKeyFile);
        }

        public byte[] JwtSecretRowData
        {
            get
            {
                if (string.IsNullOrWhiteSpace(JwtSecret))
                {
                    throw new InvalidOperationException("JwtSecret 内容为空。");
                }

                return Encoding.ASCII.GetBytes(JwtSecret);
            }
        }

        /// <summary>
        /// 根据私钥数据获取明文数据
        /// </summary>
        /// <param name="privateKeyData">私钥字符串</param>
        /// <param name="encryptedBytes">密文字节数据</param>
        /// <returns>明文数据</returns>
        private string GetPlainText(string privateKeyData, byte[] encryptedBytes)
        {
            // 创建 RSA 实例，并加载私钥
            using (RSA rsa = RSA.Create())
            {
                rsa.ImportFromPem(privateKeyData);

                // 使用私钥进行解密
                byte[] decryptedBytes = rsa.Decrypt(encryptedBytes, RSAEncryptionPadding.Pkcs1);

                // 将解密后的字节数组转换为字符串
                string decryptedData = Encoding.UTF8.GetString(decryptedBytes);

                return decryptedData;
            }
        }

        /// <summary>
        /// 获取 ES 数据库连接 URL。
        /// </summary>
        private string GetJwtSecret()
        {
            var section = _configuration.GetSection("Jwt");
            var privateKey = section["PrivateKey"];
            var secret = section["Secret"];
            var privateKeyFile = Path.Combine(_startInfo.StartPath, privateKey);
            var secretFile = Path.Combine(_startInfo.StartPath, secret);

            return GetPlainText(_reader.ReadAllText(privateKeyFile), _reader.ReadAllBytes(secretFile));
        }

        /// <summary>
        /// 获取 ES 数据库连接 URL。
        /// </summary>
        public string GetElasticSearchUrl()
        {
            var section = _configuration.GetSection("ElasticSearch");
            var privateKey = section["PrivateKey"];
#if DEBUG
            var encryptedUrl = section["EncryptedUrlTest"];
#else
            var encryptedUrl = section["EncryptedUrl"];
#endif
            var privateKeyFile = Path.Combine(_startInfo.StartPath, privateKey);
            var encryptedUrlFile = Path.Combine(_startInfo.StartPath, encryptedUrl);

            return GetPlainText(_reader.ReadAllText(privateKeyFile), _reader.ReadAllBytes(encryptedUrlFile));
        }

        /// <summary>
        /// 获取 HTTPS 配置。
        /// </summary>
        public CertificateAuthorityConfiguration GetCertificateAuthorityCertificate()
        {
            var kestrel = _configuration.GetSection("Kestrel");
            var certificate = kestrel.GetValue<string>("Certificate");
            var privateKey = kestrel.GetValue<string>("PrivateKey");
            var password = kestrel.GetValue<string>("Password");
            var port = kestrel.GetValue<int>("Port");
            var certificateFile = Path.Combine(_startInfo.StartPath, certificate);
            var privateKeyFile = Path.Combine(_startInfo.StartPath, privateKey);
            var passwordFile = Path.Combine(_startInfo.StartPath, password);

            if (!File.Exists(certificateFile))
            {
                throw new FileNotFoundException("证书文件不存在。");
            }

            if (!File.Exists(privateKeyFile))
            {
                throw new FileNotFoundException("证书密码私钥文件不存在。");
            }

            if (!File.Exists(passwordFile))
            {
                throw new FileNotFoundException("证书密码文件不存在。");
            }

            var passwordText = GetPlainText(_reader.ReadAllText(privateKeyFile), _reader.ReadAllBytes(passwordFile));

            return new CertificateAuthorityConfiguration(new X509Certificate2(certificateFile, passwordText), port);
        }

        /// <summary>
        /// 获取 MySql 连接字符串
        /// </summary>
        public string GetMySqlConnectionString(string connectionStringName)
        {
            string privateKeyFile = Path.Combine(_startInfo.StartPath, _configuration.GetConnectionString("PrivateKey"));
            string? fileName;

            switch (connectionStringName)
            {
                case "Default":
                    fileName = Path.Combine(_startInfo.StartPath, _configuration.GetConnectionString("Default"));
                    break;
                case "TestMySql":
                    fileName = Path.Combine(_startInfo.StartPath, _configuration.GetConnectionString("TestMySql"));
                    break;
                case "LocalMySql":
                    fileName = Path.Combine(_startInfo.StartPath, _configuration.GetConnectionString("LocalMySql"));
                    break;
                default:
                    throw new InvalidOperationException("无效的字符串连接名称。");
            }

            string? connectionFile = Path.Combine(_startInfo.StartPath, fileName);
            string? connectionString = GetPlainText(_reader.ReadAllText(privateKeyFile), _reader.ReadAllBytes(connectionFile));

            return connectionString;
        }
    }
}
